package com.naiterui.ehp.bs.esign.utils;

import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfDocument;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.draw.LineSeparator;
import com.naiterui.ehp.bp.bo.groupConsult.GroupConsultPreSignParamBO;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Optional;

/**
 * 会诊PDF生成
 */
@Slf4j
@Component
public class GroupConsultPdfUtil {

    private static final String EHP_HOSPITAL_NAME = ConfigUtil.getString("ehp_hospital_name");
    private static final String HOSPITAL_SEAL_PATH = ConfigUtil.getString("prescription.seal.path");
    @Value("${file.base.prefix}")
    private String basePath = "";

    BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
    BaseFont bfEnglish = BaseFont.createFont(BaseFont.HELVETICA, "", BaseFont.NOT_EMBEDDED);

    Font firsetTitleFont = new Font(bfChinese, 20, Font.BOLD);
    Font fontContent = new Font(bfChinese, 10, Font.NORMAL);
    Font fontContentGray = new Font(bfChinese, 10, Font.NORMAL, BaseColor.GRAY);
    Font fontContent2 = new Font(bfEnglish, 10, Font.NORMAL);
    Font fontContentBOLD = new Font(bfChinese, 11, Font.BOLD);
    Font fontSubContentBOLD = new Font(bfChinese, 12, Font.BOLD);
    Font fontSubContent = new Font(bfChinese, 12, Font.NORMAL);
    LineSeparator blankLine = new LineSeparator(1.5f, 100, BaseColor.WHITE, Element.ALIGN_CENTER, -3f);
    LineSeparator line = new LineSeparator(1.5f, 95, BaseColor.LIGHT_GRAY, Element.ALIGN_CENTER, -3f);

    public GroupConsultPdfUtil() throws DocumentException, IOException {
    }

    public String generatePDF(GroupConsultPreSignParamBO pdfBO) throws IOException, DocumentException, ReflectiveOperationException {
        String path = Constants.ESIGN_GROUP_CONSULT_PATH + "origin/" + pdfBO.getGroupConsultId() + "_" + pdfBO.getOpinionType() + ".pdf";
        File file = new File(basePath + path);
        if (!file.exists()) {
            FileUtils.forceMkdirParent(file);
            FileOutputStream fos = new FileOutputStream(file);
            this.generatePDF(fos, pdfBO);
            fos.close();
            log.info("会诊PDF生成完成 groupConsultId:{}, path:{}", pdfBO.getGroupConsultId(), path);
        }

        return path;
    }

    private void generatePDF(OutputStream ouput, GroupConsultPreSignParamBO pdfBO)
            throws DocumentException, IOException, ReflectiveOperationException {
        // 页面大小
        Rectangle rect = new Rectangle(PageSize.A4);
        Document document = new Document(rect);
        // 创建PdfWriter对象
        PdfWriter writer = PdfWriter.getInstance(document, ouput);
        // 打开文档
        document.open();
        // 页边空白
        document.setMargins(20, 20, 30, 40);
        // 设置医院图章

        this.generatePDFContent(document, pdfBO, writer);

        File file = new File(HOSPITAL_SEAL_PATH);
        if (file.exists()) {
            Image image = Image.getInstance(HOSPITAL_SEAL_PATH);
            image.setAbsolutePosition(360, 680);
            image.scaleToFit(150, 150);
            document.add(image);
        } else {
            log.warn("互联网医院图章不存在，请检查图章文件目录 {}", HOSPITAL_SEAL_PATH);
        }

        document.close();
        writer.close();
    }

    public static float currentHeight(PdfWriter writer) throws ReflectiveOperationException {
        Method getPdfDocument = writer.getClass().getDeclaredMethod("getPdfDocument");
        getPdfDocument.setAccessible(true);
        PdfDocument pdfD = (PdfDocument) getPdfDocument.invoke(writer);
        Field getHeight = pdfD.getClass().getDeclaredField("currentHeight");
        getHeight.setAccessible(true);
        return (float) getHeight.get(pdfD);
    }

    /**
     * 生成pdf
     *
     * @param document
     * @throws DocumentException
     * @throws IOException
     */
    @SneakyThrows
    private void generatePDFContent(Document document, GroupConsultPreSignParamBO pdfBO, PdfWriter writer) throws DocumentException, IOException {
        // title
        Paragraph titleRow = new Paragraph(EHP_HOSPITAL_NAME + "远程会诊资料", firsetTitleFont);
        titleRow.setAlignment(PdfContentByte.ALIGN_CENTER);
        titleRow.setSpacingAfter(20);
        document.add(titleRow);

        // 透明分割线
        Paragraph lineParagraph = new Paragraph();
        lineParagraph.add(blankLine);
        lineParagraph.setSpacingAfter(20);

        // 灰色分割线
        Paragraph grayLineParagraph = new Paragraph();
        grayLineParagraph.add(line);
        grayLineParagraph.setSpacingAfter(5);

        // 分割线
        document.add(lineParagraph);

        // 空行
        Phrase blank = this.buildPhrase(" ", " ", fontSubContentBOLD, fontSubContent);

        Phrase groupConsultTitle = this.buildPhrase("会诊信息：", "", fontContentBOLD, fontContentGray);
        String patientStr = String.join("  |  ", pdfBO.getName(), pdfBO.getGender() == 0 ? "女" : "男", pdfBO.getAge());
        Phrase patient = this.buildPhrase("患者：", patientStr, fontContentGray, fontContentGray);
        Phrase department = this.buildPhrase("科室：", StrUtil.blankToDefault(pdfBO.getDepartmentName(), ""), fontContentGray, fontContentGray);
        Phrase groupConsultReason = this.buildPhrase("会诊原因：", "", fontContentGray, fontContentGray);
        Phrase groupConsultReasonDetail = this.buildPhrase("", pdfBO.getGroupConsultReason(), fontContentGray, fontContentGray);

        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, groupConsultTitle));
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, patient));
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, department));
        document.add(this.buildTable(new float[]{11f, 90f}, 95, 0, 0, Element.ALIGN_LEFT, 90, true, groupConsultReason, groupConsultReasonDetail));

        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, blank));

        log.info("【会诊信息】{}", currentHeight(writer));

        Phrase targetOptionTitle = this.buildPhrase("受邀专家意见：", "", fontContentBOLD, fontContent);
        Phrase targetOptionDetail = this.buildPhrase("", pdfBO.getTargetOpinion(), fontContentGray, fontContentGray);
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, targetOptionTitle));
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, Element.ALIGN_LEFT, 80, true, targetOptionDetail));

        log.info("【受邀专家意见】{}", currentHeight(writer));

        Phrase targetDoctor = this.buildPhrase("专家签字：", "", fontContent, fontContent);
        String targetOpinionAt = Optional.ofNullable(pdfBO.getTargetOpinionAt()).map(date -> DateUtil.formatDate(date, DateUtil.DATE_TIME_FORMAT_IN_COMMA_DATE)).orElse("");
        Phrase targetTime = this.buildPhrase("时间：", targetOpinionAt, fontContent, fontContent2);
        document.add(this.buildTable(new float[]{7, 3}, 95, 0, 0, blank, targetDoctor));
        document.add(this.buildTable(new float[]{7, 3}, 95, 0, 0, blank, targetTime));
        document.add(grayLineParagraph);

        log.info("【专家签字】{}", currentHeight(writer));

        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, blank));
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, blank));

        Phrase sourceOptionTitle = this.buildPhrase("发起医师意见：", "", fontContentBOLD, fontContent);
        Phrase sourceOptionDetail = this.buildPhrase("", pdfBO.getSourceOpinion(), fontContentGray, fontContentGray);
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, sourceOptionTitle));
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, Element.ALIGN_LEFT, 80, true, sourceOptionDetail));
        log.info("【发起医师意见】{}", currentHeight(writer));

        Phrase sourceDoctor = this.buildPhrase("医师签字：", "", fontContent, fontContent);
        String sourceOpinionAt = Optional.ofNullable(pdfBO.getSourceOpinionAt()).map(date -> DateUtil.formatDate(date, DateUtil.DATE_TIME_FORMAT_IN_COMMA_DATE)).orElse("");
        Phrase sourceTime = this.buildPhrase("时间：", sourceOpinionAt, fontContent, fontContent2);
        document.add(this.buildTable(new float[]{7, 3}, 95, 0, 0, blank, sourceDoctor));
        document.add(this.buildTable(new float[]{7, 3}, 95, 0, 0, blank, sourceTime));
        document.add(grayLineParagraph);

        log.info("【医师签字】{}", currentHeight(writer));

        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, blank));
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, blank));

        Phrase diagnosisTitle = this.buildPhrase("临床诊断：", "", fontContentBOLD, fontContent);
        Phrase diagnosisDetail = this.buildPhrase("", String.join("、", pdfBO.getDiagnosis()), fontContentGray, fontContentGray);

        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, diagnosisTitle));
        document.add(this.buildTable(new float[]{1f}, 95, 0, 0, diagnosisDetail));
        document.add(grayLineParagraph);

        log.info("【临床诊断】{}", currentHeight(writer));

    }

    private Phrase buildPhrase(String title, String content, Font titleFont, Font contentFont) {
        Phrase phrase = new Phrase();
        Chunk chunkTitle = new Chunk(title, titleFont);
        Chunk chunkContent = new Chunk(content, contentFont);
        phrase.add(chunkTitle);
        phrase.add(chunkContent);
        return phrase;
    }

    private PdfPCell buildCell(Phrase phrase, int align) {
        return buildCell(phrase, align, 0, false);
    }

    private PdfPCell buildCell(Phrase phrase, int align, int minHeight, boolean fixed) {
        PdfPCell pdfPCell = new PdfPCell(phrase);
        // 隐藏单元格周边的上边框
        pdfPCell.disableBorderSide(Rectangle.TOP);
        // 隐藏单元格周边的下边框
        pdfPCell.disableBorderSide(Rectangle.BOTTOM);
        // 隐藏单元格周边的左边框
        pdfPCell.disableBorderSide(Rectangle.LEFT);
        // 隐藏单元格周边的右框
        pdfPCell.disableBorderSide(Rectangle.RIGHT);
        pdfPCell.setHorizontalAlignment(align);
        pdfPCell.setLeading(4, 1);
        if (minHeight > 0) {
            if (fixed) {
                pdfPCell.setFixedHeight(minHeight);
            } else {
                pdfPCell.setMinimumHeight(minHeight);
            }
        }
        return pdfPCell;
    }

    private PdfPTable buildTable(float[] relativeWidths, int widthPercentage, int pacingBefore, int pacingAfter,
                                 Phrase... phrases) {
        return this.buildTable(relativeWidths, widthPercentage, pacingBefore, pacingAfter, Element.ALIGN_LEFT, phrases);
    }

    private PdfPTable buildTable(float[] relativeWidths, int widthPercentage, int pacingBefore, int pacingAfter,
                                 int align, Phrase... phrases) {
        PdfPTable pdfPTable = new PdfPTable(relativeWidths);
        pdfPTable.setWidthPercentage(widthPercentage);
        for (Phrase phrase : phrases) {
            pdfPTable.addCell(this.buildCell(phrase, align));
        }
        pdfPTable.setSpacingBefore(pacingBefore);
        pdfPTable.setSpacingAfter(pacingAfter);
        return pdfPTable;
    }

    private PdfPTable buildTable(float[] relativeWidths, int widthPercentage, int pacingBefore, int pacingAfter,
                                 int align, int minHeight, boolean fixed, Phrase... phrases) {
        PdfPTable pdfPTable = new PdfPTable(relativeWidths);
        pdfPTable.setWidthPercentage(widthPercentage);
        for (Phrase phrase : phrases) {
            pdfPTable.addCell(this.buildCell(phrase, align, minHeight, fixed));
        }
        pdfPTable.setSpacingBefore(pacingBefore);
        pdfPTable.setSpacingAfter(pacingAfter);
        return pdfPTable;
    }
}
